package com.hengyue.system.service.impl;

import com.hengyue.common.constants.UserConstants;
import com.hengyue.common.utils.StringUtils;
import com.hengyue.common.utils.spring.SpringUtils;
import com.hengyue.common.utils.text.Convert;
import com.hengyue.exception.ServiceException;
import com.hengyue.system.domain.entity.SysDept;
import com.hengyue.system.domain.vo.TreeSelect;
import com.hengyue.system.repository.DeptRepository;
import com.hengyue.system.service.ISysDeptService;
import jakarta.persistence.Transient;
import jakarta.persistence.criteria.Predicate;
import lombok.RequiredArgsConstructor;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
public class SysDeptServiceImpl implements ISysDeptService {

    private final DeptRepository repository;

    @Override
    public List<SysDept> selectDeptList(SysDept dept) {
        Specification<SysDept> spec = (root, query, cb) -> {
            List<Predicate> predicates = new ArrayList<>();
            // 基础条件：del_flag = '0'
            predicates.add(cb.equal(root.get("delFlag"), "0"));
            if (dept.getId() != null && dept.getId() != 0) {
                predicates.add(cb.equal(root.get("deptId"), dept.getId()));
            }
            if (dept.getParentId() != null && dept.getParentId() != 0) {
                predicates.add(cb.equal(root.get("parentId"), dept.getParentId()));
            }
            if (StringUtils.isNotBlank(dept.getName())) {
                predicates.add(cb.like(root.get("name"), "%" + dept.getName() + "%"));
            }
            if (StringUtils.isNotBlank(dept.getStatus())) {
                predicates.add(cb.equal(root.get("status"), dept.getStatus()));
            }
            query.orderBy(
                    cb.asc(root.get("parentId")),
                    cb.asc(root.get("sort"))
            );
            return cb.and(predicates.toArray(new Predicate[0]));
        };
        return repository.findAll(spec);
    }

    @Override
    public List<TreeSelect> selectDeptTreeList(SysDept dept) {
        List<SysDept> depts = SpringUtils.getAopProxy(this).selectDeptList(dept);
        return buildDeptTreeSelect(depts);
    }

    @Override
    public List<SysDept> buildDeptTree(List<SysDept> depts) {
        Map<Long, SysDept> deptMap = new HashMap<>();
        List<SysDept> tree = new ArrayList<>();
        // 将所有部门存入Map
        depts.forEach(dept -> {
            deptMap.put(dept.getId(), dept);
            if (dept.getChildren() == null) {
                dept.setChildren(new ArrayList<>());
            }
        });
        // 构建树形结构
        depts.forEach(dept -> {
            Long parentId = dept.getParentId();
            if (parentId == null || parentId == 0L) {
                tree.add(dept);
            } else {
                SysDept parentDept = deptMap.get(parentId);
                if (parentDept != null) {
                    parentDept.getChildren().add(dept);
                }
            }
        });
        return tree.isEmpty() ? depts : tree;
    }

    @Override
    public List<TreeSelect> buildDeptTreeSelect(List<SysDept> depts) {
        List<SysDept> deptTrees = buildDeptTree(depts);
        return deptTrees.stream().map(TreeSelect::new).collect(Collectors.toList());
    }

    @Override
    public List<Long> selectDeptListByRoleId(Long roleId) {
        return null;
    }

    @Override
    public SysDept selectDeptById(Long deptId) {
        return repository.findById(deptId).orElse(null);
    }

    @Override
    public boolean selectNormalChildrenDeptById(Long deptId) {
        return repository.existsByParentIdAndStatusAndDelFlag(deptId, "0", "0");
    }

    @Override
    public boolean hasChildByDeptId(Long deptId) {
        return repository.existsByParentIdAndDelFlag(deptId, "0");
    }

    @Override
    public boolean checkDeptExistUser(Long deptId) {
        return false;
    }

    @Override
    public boolean checkDeptUnique(SysDept dept) {

        Long deptId = dept.getId() == null ? -1L : dept.getId();

        boolean exit = repository.exists((root, query, cb) ->
                cb.and(
                        cb.or(
                                cb.equal(root.get("code"), dept.getCode()),
                                cb.equal(root.get("name"), dept.getName())
                        ),
                        cb.notEqual(root.get("id"), deptId),
                        cb.equal(root.get("parentId"), dept.getParentId())
                )
        );
        return !exit;
    }

    @Override
    public void checkDeptDataScope(Long deptId) {

    }

    @Override
    @Transactional
    public void insertDept(SysDept dept) {
        SysDept info = repository.findById(dept.getParentId()).orElse(null);
        // 如果父节点不为正常状态,则不允许新增子节点
        if (info != null && !UserConstants.DEPT_NORMAL.equals(info.getStatus())) {
            throw new ServiceException("部门停用，不允许新增");
        }
        dept.setDelFlag(UserConstants.DEPT_NORMAL);
        if (info != null) {
            dept.setAncestors(info.getAncestors() + "!" + dept.getParentId());
        }
        repository.save(dept);
    }

    @Override
    @Transactional
    public void updateDept(SysDept dept) {

        String newAncestors = "";
        String oldAncestors = "";
        SysDept existingDept = repository.findById(dept.getId()).orElseThrow(() -> new ServiceException("部门不存在"));
        boolean parentChanged = !existingDept.getCode().equals(dept.getCode());
        if (parentChanged && hasChildByDeptId(dept.getId())) {
            newAncestors = existingDept.getAncestors() + "!" + dept.getCode();
            oldAncestors = existingDept.getAncestors() + "!" + existingDept.getCode();
            updateChildrenAncestorsBatch(newAncestors, oldAncestors);
        }
        repository.save(dept);
    }

    private void updateChildrenAncestorsBatch(String newAncestors, String oldAncestors) {
        repository.updateAncestorsForChildren(
                oldAncestors + "%",
                oldAncestors,
                newAncestors
        );
    }

    @Override
    @Transactional
    public void deleteDeptById(Long deptId) {
        repository.updateDelFlagById(deptId, "2");
    }
}
